#include "widget.h"
#include "ui_widget.h"
#include <QFileDialog>
#include <QMessageBox>
#include <QVector>
#include <QtMath>
#include <QStringList>
#include <QDragEnterEvent>
#include <QMimeData>
#include "const.h"

#include <QDebug>

Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) {
	ui->setupUi(this);
	setFixedSize(this->width(), this->height());

	connect(ui->fileBrowseBtn, &QPushButton::clicked, this, [&] {
		QString filename = QFileDialog::getOpenFileName(
			this, "选择MIDI文件", "", "MIDI File (*.mid);;All Files (*)");
		if (filename.isEmpty()) return;

		readFile(filename);
	});

	connect(ui->btnOut, &QPushButton::clicked, this, &Widget::make);
}

//无效则返回空，返回字符串不代表能用
QString GetDropFileName(QDropEvent *event) {
	auto &&mime = event->mimeData();
	if (!mime->hasUrls()) return {}; //不是文件

	auto &&url = mime->urls().value(0);
	if (url.isEmpty() || !url.isValid()) return {}; //无效
	if (!url.isLocalFile()) return {};				//不是本地文件

	return url.toLocalFile();
}

void Widget::dragEnterEvent(QDragEnterEvent *event) {
	auto &&name = GetDropFileName(static_cast<QDropEvent *>(event));

	if (name.isEmpty()) {
		event->ignore();
	} else {
		event->accept();
	}
}

void Widget::dropEvent(QDropEvent *event) {
	readFile(GetDropFileName(event));
}

void Widget::readFile(const QString &filename) {
	ui->filename->setText("文件读取中...");
	midifile.clear();
	ui->textOut->clear();
	auto &mf = *(new smf::MidiFile);
	mf.read(filename.toLocal8Bit().toStdString());

	if (!mf.status()) {
		ui->filename->setText("未加载文件");
		QMessageBox::critical(this, "MIDI失败", "MIDI文件读取失败！");
		delete &mf;
		return;
	}
	ui->filename->setText(filename.mid(filename.lastIndexOf('/') + 1));

	mf.joinTracks();
	mf.doTimeAnalysis();
	mf.linkNotePairs();

	midifile.reset(&mf);
}

void Widget::make() {
	if (midifile.isNull()) {
		QMessageBox::information(this, "MIDI失败", "请先加载文件再生成！");
		return;
	}

	ui->textOut->setText("计算中...");

	auto &mf = *midifile.get();
	bool enableFac = ui->cbFac->isChecked();

	struct Note {
		float start;
		float duration;
		short pitch;
		float volume;
		short instrument;
	};
	QVector<Note> notes;

	smf::MidiEventList &track = mf[0];
	QVector<int> nowInst(16, 1);
	QVector<float> nowVolFac(16, 1);

	int eventCount = track.size();
	for (int ei = 0; ei < eventCount; ei++) {
		smf::MidiEvent &event = track[ei];
		int channel = event.getChannelNibble();

		if (event.isNoteOn()) {
			Note n;

			n.start = event.seconds;
			n.duration = event.getDurationInSeconds();
			n.volume = event[2] / 1.27 * nowVolFac.value(channel);

			if (qIsNull(n.duration) || qIsNull(n.volume)) continue;

			if (channel == 10) {
				n.instrument = 0;
				n.pitch = drumMap.value(event[1], 1);

				if (enableFac) {
					n.volume *= 0.01 * drumVolFac.value(n.pitch - 1, 60);
				}
			} else {
				n.pitch = event[1];
				n.instrument = nowInst.value(channel, 1);

				if (enableFac) {
					n.volume *= 0.01 * patchVolFac.value(n.instrument - 1, 60);
				}
			}

			notes.append(n);
		}

		if (event.isTimbre()) {
			nowInst[channel] = patchMap.value(event[1], 1);
		}
		if (event.isController() && (event[1] == 0x07)) {
			nowVolFac[channel] = event[2] / 127.0;
		}
	}

	//无需排序了
	//std::sort(notes.begin(), notes.end(), [](Note &a, Note &b) {
	//	qDebug() << (a.start < b.start);
	//	return a.start < b.start;
	//});

	if (ui->cbMax->isChecked()) {
		float maxVol = 0;
		for (Note &n : notes) {
			if (maxVol < n.volume) maxVol = n.volume;
		}

		float volFac = 100.0 / maxVol;
		if (qIsFinite(volFac)) {
			for (Note &n : notes) {
				n.volume *= volFac;
			}
		}
	}

	if (ui->cbDRm->isChecked()) {
		double globalStart = notes[0].start;
		for (Note &n : notes) {
			n.start -= globalStart;
		}
	}

	{
		float fac = ui->sliderVol->value() / 10.0;

		for (Note &n : notes) {
			float &rv = n.volume;

			if (fac > 0) {
				rv = 100 * fac + (1 - fac) * rv;
			} else {
				rv = (1 + fac) * rv;
			}

			if (rv > 100) {
				rv = 100;
			} else if (rv < 0) {
				rv = 0.1;
			}
		}
	}

	{
		short shift = ui->spinShift->value();

		for (Note &n : notes) {
			if (n.instrument != 0) {
				short &p = n.pitch;
				p += shift;

				if (p < 0) {
					p = 0;
				} else if (p > 127) {
					p = 128;
				}
			}
		}
	}

	{
		float timeFac = qPow(1.12, -ui->sliderSpeed->value());
		for (Note &n : notes) {
			n.duration *= timeFac;
			n.start *= timeFac;
		}
	}

	QStringList out(version);
	for (Note &n : notes) {
		out += QString("%1s%2u%3%4v%5i")
				   .arg(int(n.start * 50), 0, 16)
				   .arg(int(n.duration * 50), 0, 16)
				   .arg(n.pitch, 2, 16, QLatin1Char('0'))
				   .arg(int(n.volume), 0, 16)
				   .arg(n.instrument, 0, 16);
	}
	ui->textOut->setText(out.join(nullptr));
}

Widget::~Widget() {
	delete ui;
}
